package com.lagou.client.invoke;


import com.lagou.client.handler.RpcClientHandler;
import com.lagou.invoke.RpcRequest;
import com.lagou.serializer.JSONSerializer;
import com.lagou.serializer.RpcEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Kirito
 * @description:
 * @date 2020/05/26
 */
public class RpcConsumer {

    private static final ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private RpcClientHandler handler;

    public Object createProxy(final Class<?> clazz, final String methodName) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (null == handler) {
                    initRpcClientHandler();
                }

                RpcRequest request = new RpcRequest();
                request.setRequestId(UUID.randomUUID().toString().replaceAll("-", ""));
                request.setClassName(clazz.getName());
                request.setMethodName(methodName);
                request.setParameters(args);
                if (null != args && args.length != 0) {
                    Class<?>[] parameterTypes = new Class[args.length];
                    for (int i = 0; i < parameterTypes.length; i++) {
                        parameterTypes[i] = args[i].getClass();
                    }
                    request.setParameterTypes(parameterTypes);
                }
                handler.setRequest(request);
                return executors.submit(handler).get();
            }
        });
    }

    public void initRpcClientHandler() {
        this.handler = new RpcClientHandler();
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel channel) throws Exception {
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new RpcEncoder(RpcRequest.class, new JSONSerializer()));
                        pipeline.addLast(handler);
                    }
                });

        try {
            bootstrap.connect("localhost", 8081).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
